
#ifndef MBND_H
#define MBND_H
#include <boost/multi_array.hpp>
#include <iostream>
#include <string>
#include <fstream>
#include <ctime>
#include <boost/timer.hpp>
#include "MBND.h"

//Matrice Bande


using namespace boost;
using namespace std;

template <typename T>
class MBND
{
	private :
	//attributs
	boost::multi_array<T,2> Bande; //stocke les valeurs des coefficients non nuls par bande
	int dim;
	int nbBandeSup;
	int nbBandeInf;
	
	public:
	//constructeur
	MBND(int dim1, int nbS, int nbI);
	//methode 
	void printBande();
	void AjoutElements(T k, int i, int j);
	//produit matrice/vecteur
	void SPMV(const vector <T>& u, vector <T> v);
 };
 
 /****************************************************************************
    * Fonction: MBND<T>::MBND()
    * Description: constructeur par parametres
    * Paramètres: - int dim1 dimension de la matrice
    *             - int nbS nombre de bandes superieures
    *             - int nbI nombre de bandes inferieures
    * Retour: aucun
    ****************************************************************************/
    template <typename T>
    MBND<T>::MBND(int dim1, int nbS, int nbI)
   {
	   //par defaut la matrice est une matrice unite
	   dim=dim1;
	   nbBandeSup=nbS;
	   nbBandeInf=nbI;
	   int n=nbBandeSup+nbBandeInf+1;
	   Bande.resize(boost::extents[dim][n]);
	   
	   //parametres pour remplir les bandes
	   int l=nbI-1;
	   int i;
	   int k;
	   int m=0;
	   int s=nbI+1;
	   
	   //correspond a la grande diagonale
	   for(int i=0;i<dim;i++)
	   {
		   Bande[i][nbI]=1;
	   }
	   
	   //pour remplir les bandes inferieures
	   while (l!=-1)
	   {
		   i=0;
		   k=dim-i-1;
		   while(k!=m)
		   {
			   Bande[k][l]=1;
			   i++;
			   k=dim-i;
		   }
		   l--;
		   m++;
	   }
	  
	  //pour remplir les bandes superieures 
      k=0;
	  m=dim-k-1;	  
	   while (s!=n)
	   {
		   k=0;
		   while(k!=m)
		   {
			   Bande[k][s]=1;
			   k++;  
		   }
		   s++;
		   m--;
	   }
		   
   }
 
    /****************************************************************************
    * Fonction: MBND<T>::printBande()
    * Description: affiche les indices des valeur non nuls de la matrice
    * Paramètres: aucun
    * Retour: aucun
    ****************************************************************************/
    template <typename T>
    void MBND<T>::printBande()
       {
			for(int i=0; i<Bande.shape()[0]; i++)
			{
				for (int j=0; j<Bande.shape()[1]; j++)
				{
					cout<<Bande[i][j]<<" ";
				}
			
				cout <<" " << endl;
			}
		}

    /****************************************************************************
    * Fonction: MBND<T>::AjoutElements()
    * Description: Constructeur par paramètres
    * Paramètres: - T k : valeur a ajouter dans le tableau Values
    *             - int nb_lignes: numero de la ligne correspondante
    *             - int nb-colonnes: numero de la colonne correspondante
    *
    * Retour: aucun
    //****************************************************************************/
    template <typename T>
    void MBND<T>::AjoutElements(T k, int i, int j) // on suppose que l'utilisateur remplit les lignes/colonnes de 1 a dim
    {
	  if(i == j) // si on est sur la grande diagonale on remplit directement la colonne correspondante 
	  {
		  Bande[i-1][i-1]=k;
	  }
	  else
	  {
		  if(i>j) // si on veut remplir sur une bande inférieure
		  {
			  if(nbBandeInf<i-j) //alors qu'on a pas l'espace memoire necessaire
			  {
				  Bande.resize(boost::extents[Bande.shape()[0]][i-j+1]); // on alloue l'espace memoire necessaire
				  //tout ce qui se trouve à la colonne 0 doit etre ramene a la derniere colonne
				  for(int c=0; c<dim; c++)
				  {
					  Bande[c][i-j]=Bande[c][nbBandeInf];
					  cout<<Bande[c][i-j]<<endl;
					  Bande[c][nbBandeInf]=0;
				  }
				  nbBandeInf=i-j+1;
			  }
			  Bande[i-1][nbBandeInf-(i-j+1)]=k;
		  }
		  if(j>i) //si on veut remplir sur une bande superieure
		  {
			  if(j-i+1>Bande.shape()[1]) //qu'on a pas l'espace memoire necessaire
			  {
				  Bande.resize(boost::extents[Bande.shape()[0]][nbBandeInf+j-i+1]); // on alloue l'espace memoire necessaire
			  }
			  Bande[i-1][nbBandeInf+j-i]=k;
		  }
	 }
	}
	
/****************************************************************************
    * Fonction : SPMV
    * Description :- calcul le produit matrice_creuse/vecteur et enregistre le temps d'execution et les parametre de la matrice dans un fichier
    *                pour un stockage creux du type BaNDe, on a un tableau de taille dim*(nbBandeInf+nbBandeSup+1) qui stocke le nombre d'elements non nuls par bande
    * Paramètres : - T *u, le vecteur multiplie
    *              - T *v, le vecteur resultat
    * 			   - char* forme, la forme de la matrice
    * Retour: aucun
****************************************************************************/    
	template <typename T>
    void MBND<T>::SPMV(const vector <T>& u, vector <T> v)
    {
		//forme matrice creuse
	    string forme= "stockage BaNDe";
		//parametres chronometre
		int compteur;
		int f_compteur=1;
		double temps_moy=0.0000; //temps moyen
		
		int max;
		int min;
		
		
		//while(compteur<f_compteur)
		//{//debut chrono
			timer t;
			t.elapsed();
			for(int i=0;i<dim;i++)
			{
				for(int k=0;k<nbBandeInf+nbBandeSup+1;k++)
				{
					max= k>k+i-nbBandeInf ? k : k+i-nbBandeInf;
					min=dim < max ? dim : max;
					v[i]=v[i]+Bande[i][k]*u[min];
					//cout<<"v["<<i<<"]="<<v[i]<<endl;
				}
				//cout<<"pour bnd : v["<<i<<"]="<<v[i]<<endl;
				//fin chrono
				temps_moy=temps_moy+t.elapsed();
				//compteur++;
			}
		    
			temps_moy = temps_moy/((double)f_compteur);
		    
			// ouverture fichier en ecriture seule
			ofstream fichier("creuse_CPP.csv", ios::out | ios::app);
			if(fichier)
			{
				fichier << dim << ";" << forme << ";" << temps_moy << endl;
				fichier.close();
			}			
		}
#endif
